package com.roadmap.biz.life.goal;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.roadmap.biz.common.BaseManager;
import com.roadmap.biz.common.RdcQueryConst;
import com.roadmap.common.BaseConst;
import com.roadmap.common.BaseEntity;
import com.roadmap.common.RdcResourceBundle;
import com.roadmap.common.BaseEnum.ActionType;
import com.roadmap.entities.Aspirant;
import com.roadmap.entities.LifeGoal;
import com.roadmap.entities.ext.GoalExt;
import com.roadmap.exception.ActionNotSupportedException;
import com.roadmap.exception.EntityNotFoundException;
import com.roadmap.exception.InvalidActionException;
import com.roadmap.exception.InvalidQueryException;
import com.roadmap.exception.InvalidValueException;
import com.roadmap.exception.NotAuthorizedException;
import com.roadmap.exception.NotMatchedException;
import com.roadmap.exception.NullObjectException;
import com.roadmap.exception.ServerException;
import com.roadmap.repo.db.RdcQuery;

public class GoalManager extends BaseManager {
	private static final Set<Integer> executableStateSet = new HashSet<Integer>();
	static {
		executableStateSet.add(BaseConst.LIFEGOAL_STATUS_UNSCHEDULED);
		executableStateSet.add(BaseConst.LIFEGOAL_STATUS_SCHEDULED);
		executableStateSet.add(BaseConst.LIFEGOAL_STATUS_UNDERGOING);
	}

	public GoalManager(String currentUser, String itemOwner) {
		super(currentUser, itemOwner, LifeGoal.class, Aspirant.class);
	}

	@Override
	public BaseEntity cancel(String entryId) throws NullObjectException,
			ServerException, InvalidValueException, EntityNotFoundException,
			InvalidActionException, NotAuthorizedException,
			ActionNotSupportedException {
		return changeStatus(entryId, BaseConst.LIFEGOAL_STATUS_CANCELED);
	}

	@Override
	public BaseEntity complete(String entryId) throws NullObjectException,
			ServerException, InvalidValueException, EntityNotFoundException,
			InvalidActionException, NotAuthorizedException,
			ActionNotSupportedException {
		return changeStatus(entryId, BaseConst.LIFEGOAL_STATUS_COMPLETED);
	}

	/** 只更新目标和目标的状态值，不删除任何数据 */
	@Override
	public void delete(String entryId) throws NullObjectException,
			ServerException, InvalidValueException, InvalidActionException,
			EntityNotFoundException, NotAuthorizedException,
			ActionNotSupportedException {
		Date today = new Date();
		Session session = getDbUtil().getSession();
		Transaction tx = null;
		try {
			tx = session.beginTransaction();

			// 更新目标
			Query q = session.createQuery(RdcQueryConst.HQL_UPDATE_GOAL_STATUS);
			q.setParameter("gid", entryId);
			q.setParameter("state", BaseConst.LIFEGOAL_STATUS_DELETED);
			q.setParameter("dt", today);
			q.executeUpdate();

			// 目标有目标，删除目标下的任务
			q = session.createQuery(RdcQueryConst.HQL_UPDATE_TASK_STATUS);
			q.setParameter("gid", entryId);
			q.setParameter("state", BaseConst.LIFETASK_STATUS_DELETED);
			q.setParameter("dt", today);
			q.executeUpdate();

			tx.commit();
		}
		catch (HibernateException e) {
			_log.error(e);
			if (tx != null) {
				tx.rollback();
			}

			throw e;
		}
		finally {
			session.close();
		}
	}

	private BaseEntity changeStatus(String entityId, int status)
			throws NullObjectException, ServerException, InvalidValueException,
			EntityNotFoundException, NotAuthorizedException,
			ActionNotSupportedException, InvalidActionException {
		LifeGoal goal = (LifeGoal) get(entityId);
		int currentStatus = goal.getStatus();
		// 状态不能等于目标的当前状态，也不能为deleted
		if (!canGoalBeModified(currentStatus, status)) {
			InvalidValueException e = new InvalidValueException();
			String msg = "the target status can't be DELETED and can't be the current one, either.";
			_log.error(msg, e);
			throw e;
		}
		
		// 更新目标
		Date today = new Date();
		goal.setStatus(status);
		goal.setUpdatedTime(today);
		if (status == BaseConst.LIFEGOAL_STATUS_COMPLETED) {
			if (currentStatus != BaseConst.LIFEGOAL_STATUS_SCHEDULED
					&& currentStatus != BaseConst.LIFEGOAL_STATUS_UNDERGOING) {
				ActionNotSupportedException e = new ActionNotSupportedException();
				_log.error("Only undergoing or scheduled goals support the action", e);
				throw e;
			}

			goal.setRealEndDate(today);
			getDbUtil().update(goal);
			getNoticeManager().notifyForEntry(goal, ActionType.COMPLETE);
		}
		else {
			getDbUtil().update(goal);
		}
		return goal;
	}

	private boolean canGoalBeModified(int currentStatus, int targetStatus) {
		return currentStatus != targetStatus
				&& currentStatus != BaseConst.LIFEGOAL_STATUS_DELETED;
	}

	@Override
	public List getEntitiesOfOwner() throws ServerException,
			NullObjectException, NotMatchedException, InvalidValueException {
		Object[] params = { getOwnerId(), BaseConst.LIFEGOAL_STATUS_DELETED };
		return getEntities(RdcQueryConst.SQL_GOAL_BY_USER, params);
	}

	/** 目标的父项是Aspirant，此处的parentId只能是itemOwnerId */
	@Override
	public List getEntitiesOfParent(String parentId) throws ServerException,
			NullObjectException, NotMatchedException, InvalidValueException,
			InvalidQueryException, EntityNotFoundException {
		return getEntitiesOfOwner();
	}

	@Override
	public BaseEntity getEntityExt(String entityId) throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException,
			EntityNotFoundException, NotMatchedException,
			NotAuthorizedException {
		if (entityId == null) {
			NullObjectException e = new NullObjectException();
			_log.error("entity id is NULL", e);
			throw e;
		}

		// 查询目标
		LifeGoal goal = (LifeGoal) get(entityId);
		GoalExt goalExt = new GoalExt(goal);

		// 进度等信息
		RdcQuery query = getDbUtil().createNativeSQLQuery(
				RdcQueryConst.SQL_GOAL_STATISTICS);
		query.addScalar("amount", Hibernate.INTEGER);
		query.addScalar("assigned", Hibernate.INTEGER);
		query.addScalar("progress", Hibernate.INTEGER);
		query.setParameter("gid", entityId);
		query.setParameter("state", BaseConst.LIFETASK_STATUS_DELETED);
		Object[] result = (Object[]) query.getUniqueResult();
		if (result != null) {
			// 目标没有任务
			goalExt.setTaskCount(((Integer) result[0]).intValue());
			if (result[1] != null) {
				goalExt.setAssignedProportion(((Integer) result[1]).intValue());
			}
			if (result[2] != null) {
				goalExt.setProgress(((Integer) result[2]).intValue() / 100);
			}
		}
		return goalExt;
	}

	@Override
	public BaseEntity get(String entryId) throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException,
			EntityNotFoundException {
		Object[] params = { entryId, getOwnerId(),
				BaseConst.LIFEGOAL_STATUS_DELETED };
		BaseEntity goal = (LifeGoal) getDbUtil().getUniqueEntity(
				RdcQueryConst.HQL_GOAL_BY_USER_ID, params);
		if (goal == null) {
			EntityNotFoundException e = new EntityNotFoundException();
			_log.error(String.format("goal %s was not found.", entryId));
			throw e;
		}

		return goal;
	}

	@Override
	public List getEntitiesByStatus(int[] states)
			throws ActionNotSupportedException, ServerException,
			InvalidValueException {
		if (states == null || states.length == 0) {
			InvalidValueException e = new InvalidValueException();
			_log.error("states are empty.", e);
			throw e;
		}

		List<Integer> stateList = new ArrayList<Integer>();
		for (int status : states) {
			stateList.add(new Integer(status));
		}

		RdcQuery query = getDbUtil().createNativeSQLQuery(
				RdcQueryConst.SQL_GOALS_BY_STATUES);
		query.addEntity("g", LifeGoal.class);
		query.setParameterList("states", stateList);
		query.setParameter("owner", getOwnerId());
		return query.getResults();
	}

	@Override
	public List getExecutableEntities() throws HibernateException,
			ServerException, InvalidQueryException, InvalidValueException,
			NotMatchedException, ActionNotSupportedException {
		RdcQuery query = getDbUtil().createNativeSQLQuery(
				RdcQueryConst.SQL_GOALS_BY_STATUES);
		query.addEntity("g", LifeGoal.class);
		query.setParameterList("states", executableStateSet.toArray());
		query.setParameter("owner", getOwnerId());
		List<LifeGoal> list = query.getResults();
		if (list == null || list.isEmpty()) {
			return null;
		}

		// 存放当前用户可访问的任务
		Map<String, GoalExt> goalMap = new LinkedHashMap<String, GoalExt>();
		findAuthorizedGoals(list, goalMap);
		computeGoalsProgress(goalMap);
		return getResultsForSpecifiedPage(goalMap.values());
	}

	private void findAuthorizedGoals(List<LifeGoal> results,
			Map<String, GoalExt> goalMap) throws ActionNotSupportedException,
			InvalidValueException, ServerException, NotMatchedException {
		// 存放权限为limited的任务的ID
		List<String> limitedTaskIDs = new ArrayList<String>();
		Iterator<LifeGoal> iter = results.iterator();
		Date today = new Date();
		while (iter.hasNext()) {
			LifeGoal goal = iter.next();

			// 如果goal的权限是private，而当前用户不是其拥有者
			if (goal.getVisibility() == BaseConst.ENTRY_VISIBILITY_PRIVATE
					&& !getCurrentUserId().equals(getOwnerId())) {
				continue;
			}

			GoalExt ext = new GoalExt(goal);
			ext.setSymptom(diagnose(ext, today));
			goalMap.put(goal.getId(), ext);

			// 若任务权限是limited，需进一步确认用户权限
			if (goal.getVisibility() == BaseConst.ENTRY_VISIBILITY_LIMITED) {
				limitedTaskIDs.add(goal.getId());
			}
		}

		filterLimitedEntities(limitedTaskIDs.toArray(new String[0]), goalMap);
	}

	private void computeGoalsProgress(Map<String, GoalExt> goalMap)
			throws ServerException {
		if (goalMap == null || goalMap.isEmpty()) {
			return;
		}

		// 计算各目标的进度
		RdcQuery query = getDbUtil().createNativeSQLQuery(
				RdcQueryConst.SQL_GOALS_STATISTICS);
		query.addScalar("gid", Hibernate.STRING);
		query.addScalar("amount", Hibernate.INTEGER);
		query.addScalar("assigned", Hibernate.INTEGER);
		query.addScalar("progress", Hibernate.INTEGER);
		query.setParameterList("goalIDs", goalMap.keySet().toArray());
		query.setParameter("state", BaseConst.LIFETASK_STATUS_DELETED);
		List<Object[]> results = query.getResults();
		if (results == null || results.isEmpty()) {
			return;
		}

		Iterator<Object[]> iter = results.iterator();
		while (iter.hasNext()) {
			Object[] rs = (Object[]) iter.next();
			GoalExt ext = goalMap.get(rs[0]);
			ext.setTaskCount(((Integer) rs[1]).intValue());
			if (rs[2] != null) {
				ext.setAssignedProportion(((Integer) rs[2]).intValue());
			}
			if (rs[3] != null) {
				ext.setProgress(((Integer) rs[3]).intValue() / 100);
			}
		}
	}

	private String diagnose(GoalExt Goal, Date ref)
			throws ActionNotSupportedException, InvalidValueException {
		int status = Goal.getStatus();
		if (isStatusFitForDiagnose(status)) {
			Date start = Goal.getScheduledStartDate();
			Date end = Goal.getScheduledEndDate();

			// 对比预定日期与当前时间
			if (status == BaseConst.LIFEGOAL_STATUS_SCHEDULED
					&& start.before(ref)) {
				return RdcResourceBundle.Entity_Symptom_Not_Started;
			}

			if (status == BaseConst.LIFEGOAL_STATUS_UNDERGOING
					&& end.before(ref)) {
				return RdcResourceBundle.Entity_Symptom_Not_Ended;
			}
		}

		return RdcResourceBundle.Entity_Symptom_Healthy;
	}

	private boolean isStatusFitForDiagnose(int status) {
		return executableStateSet.contains(status);
	}
}
